package com.example.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.dao.*;
import com.example.demo.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 功能描述：基于用户的协同过滤推荐算法和基于内容的推荐算法
 *  定义一个工具类来封装推荐算法的实现方法
 * @Author: Zhanghf
 * @Date: 2021/5/23 18:24
 */
@Component
public class InterestingService {
    @Autowired(required = false)
    private UserMapper userMapper;
    @Autowired(required = false)
    private BookShelfMapper bookShelfMapper;
    @Autowired(required = false)
    private ScoreMapper scoreMapper;
    @Autowired
    private BookMapper bookMapper;
    @Autowired
    private RecUtils recUtils;
    /**
     * @Author Zhanghf
     * @Date 2021-11-01 19:09
     * @Description: 使用基于用户的杰卡德相似度对用户产生推荐结果
     * @param recUserid 用户id
     * @Return: java.util.List<java.util.Map.Entry<java.lang.Integer,java.lang.Double>>
     */
    public List<Map.Entry<Integer, Double>> jkd_rec(Integer recUserid)
    {
        //用户id ___相似度
        HashMap<Integer, Double> userid_jkd = new HashMap<>();

        //对于id=recUserid的用户求相似度

        //除id=recUserid的其他所有用户集合
        List<User> users = userMapper.selectList(new QueryWrapper<User>().ne("id", recUserid));

        //计算其他所有用户与用户recUserid的相似度，结果放入userid_jkd Map集合
        for (int i = 0; i < users.size(); i++) {
            double jkd = recUtils.jkd(recUserid, users.get(i).getId());
            userid_jkd.put(users.get(i).getId(),jkd);
        }

        //为了对Map集合进行相似度排序，操作如下

        //排序前 遍历输出
       /* System.out.println("遍历排序前 用户相似度集合");
         Set<Map.Entry<Integer, Double>> entries = userid_jkd.entrySet();
            for (Map.Entry<Integer, Double> entry : entries) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/

        //排序
        List<Map.Entry<Integer, Double>> list = new ArrayList<Map.Entry<Integer, Double>>(userid_jkd.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, Double>>() {
            public int compare(Map.Entry<Integer, Double> o1,
                               Map.Entry<Integer, Double> o2) {
                if(o1.getValue() < o2.getValue()){
                    return 1;
                }else if(o1.getValue() == o2.getValue()){
                    return 0;
                }else{
                    return -1;
                }
            }
        });

        //输出排序后集合
        /*System.out.println("遍历排序后 用户相似度集合");

        for (Map.Entry<Integer, Double> entry : list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/


        //取最相似的四名用户
        List<Map.Entry<Integer, Double>> sublist = list.subList(0, 3);

        //遍历输出
  /*    System.out.println("取最相似的四名用户遍历输出");

        for (Map.Entry<Integer, Double> entry : sublist) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/


        //具体到某本书，用户对该书的可能感兴趣程度
        //bookid__感兴趣程度
        HashMap<Integer, Double> books_interesting = new HashMap<>();

        for (int i = 0; i < sublist.size(); i++) {
            //对于每一名相似的用户
            Integer key_userid = sublist.get(i).getKey();   //获取用户id
            List<BookShelf> bookShelves = bookShelfMapper.
                    selectList(new QueryWrapper<BookShelf>().eq("userid", key_userid));//该用户的书架

            for (BookShelf bookShelf : bookShelves) {

                if(!books_interesting.containsKey(bookShelf.getBookid()))
                {
                    //感兴趣集合中不存在该书，添加元素（图书id,感兴趣的程度（即该用户与用户1的相似度
                    books_interesting.put(bookShelf.getBookid(),sublist.get(i).getValue());
                }
                else {
                    //感兴趣集合中已存在该书，修改集合中该元素，将感兴趣程度求和
                    books_interesting.replace(bookShelf.getBookid(),books_interesting.get(bookShelf.getBookid())+sublist.get(i).getValue());
                }
            }
        }

        //排序前 遍历输出
       // System.out.println("遍历排序前 用户感兴趣程度集合");
/*        Set<Map.Entry<Integer, Double>> books_interestingSet = books_interesting.entrySet();
        for (Map.Entry<Integer, Double> entry : books_interestingSet) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/

        //对感兴趣集合进行排序
        List<Map.Entry<Integer, Double>> books_interesting_list = new ArrayList<Map.Entry<Integer, Double>>(books_interesting.entrySet());
        Collections.sort(books_interesting_list, new Comparator<Map.Entry<Integer, Double>>() {
            public int compare(Map.Entry<Integer, Double> o1,
                               Map.Entry<Integer, Double> o2) {
                if(o1.getValue() < o2.getValue()){
                    return 1;
                }else if(o1.getValue() == o2.getValue()){
                    return 0;
                }else{
                    return -1;
                }
            }
        });

        //排序后遍历输出
       /* System.out.println("遍历排序后 用户感兴趣程度集合");

        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/


        //books_interesting_list 即为用户1对不同图书的感兴趣程度 集合
        //先去重，将已经在用户1收藏夹中的图书从集合中去掉
        List<BookShelf> usershelf = bookShelfMapper.selectList(new QueryWrapper<BookShelf>().eq("userid", recUserid));

        List<Map.Entry<Integer, Double>> books_my_list=  new ArrayList<Map.Entry<Integer, Double>>();
        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            for (BookShelf bookShelf : usershelf) {
                if(bookShelf.getBookid()==entry.getKey())
                {
                    books_my_list.add(entry);
                }
            }
        }
        books_interesting_list.removeAll(books_my_list);
        //去重结束，遍历输出
       /* System.out.println("去重后 用户感兴趣程度集合");

        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
*/
       //返回推荐结果（基于杰卡德相似度
        return books_interesting_list;
    }
    /**
     * @Author Zhanghf
     * @Date 2021-11-01 19:20
     * @Description: 使用基于用户的皮尔逊相关系数对用户产生推荐结果
     * @param recUserid
     * @Return: java.util.List<java.util.Map.Entry<java.lang.Integer,java.lang.Double>>
     */
    public List<Map.Entry<Integer, Double>> pearson_rec(Integer recUserid)
    {


        //用户id ___相似度
        HashMap<Integer, Double> userid_pear = new HashMap<>();

        //对于id=recUserid的用户求相似度

        //除id=recUserid的其他所有用户集合
        List<User> users = userMapper.selectList(new QueryWrapper<User>().ne("id", recUserid));

        //计算其他所有用户与用户recUserid的相似度，结果放入userid_jkd Map集合
        for (int i = 0; i < users.size(); i++) {
            double pearson = recUtils.pearson(recUserid, users.get(i).getId());
       /*
       * recUtils.pearson(recUserid, users.get(i).getId()) 返回的皮尔逊相关系数可能为
       * 0.0/0.0
       * NaN，是Not a Number的缩写。
            NaN 用于处理计算中出现的错误情况，比如 0.0 除以 0.0 或者求负数的平方根。
           如果不进行处理，之后排序，NaN会排在正常数据的前面
           *
       * */
            if (!Double.isNaN(pearson))
            {
                userid_pear.put(users.get(i).getId(),pearson);
            }

        }

        //为了对Map集合进行相似度排序，操作如下

        //排序前 遍历输出
/*        System.out.println("遍历排序前 用户相似度集合");
        Set<Map.Entry<Integer, Double>> entries = userid_pear.entrySet();
            for (Map.Entry<Integer, Double> entry : entries) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/

        //排序
        List<Map.Entry<Integer, Double>> list = new ArrayList<Map.Entry<Integer, Double>>(userid_pear.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, Double>>() {
            public int compare(Map.Entry<Integer, Double> o1,
                               Map.Entry<Integer, Double> o2) {
                if(o1.getValue() < o2.getValue()){
                    return 1;
                }else if(o1.getValue() == o2.getValue()){
                    return 0;
                }else{
                    return -1;
                }
            }
        });

        //输出排序后集合
        System.out.println("遍历排序后 用户相似度集合");
        for (Map.Entry<Integer, Double> entry : list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
        //   System.out.println("*******************************************");
        //取最相似的两名用户
        List<Map.Entry<Integer, Double>> sublist = list.subList(0, 2);

        //遍历输出
/*        System.out.println("取最相似的两名用户 遍历输出");

        for (Map.Entry<Integer, Double> entry : sublist) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }*/

        //具体到某本书，用户recUserid对该书的可能评分
        //bookid__评分
        HashMap<Integer, Double> books_interesting = new HashMap<>();

        for (int i = 0; i < sublist.size(); i++) {
            //对于每一名相似的用户
            Integer key_userid = sublist.get(i).getKey();   //获取用户id

            List<Score> scores = scoreMapper.
                    selectList(new QueryWrapper<Score>().eq("userid", key_userid));//该用户的评分表

            for (Score score : scores) {
                if (!books_interesting.containsKey(score.getBookid())) {
                    //评分预测集合中不存在该书，添加元素（图书id,感兴趣的程度（即该用户与用户1的相似度
                    books_interesting.put(score.getBookid(),score.getCore());
                } else {
                    //评分预测中已存在该书，修改集合中该元素，将感兴趣程度求和
                    Double son = 0.0;
                    Double par = 0.0;

                    for (int j = 0; j < sublist.size(); j++) {
                        Integer keyUserid = sublist.get(j).getKey();
                        Double sim = sublist.get(i).getValue();         //相似度
                        scores = scoreMapper.
                                selectList(new QueryWrapper<Score>().eq("userid", keyUserid));
                        for (Score score1 : scores) {
                            if (score1.getBookid() == score.getBookid()) {
                                son += score1.getCore() * sim;
                                par += sim;
                            }
                        }
                        books_interesting.replace(score.getBookid(), son / par);
                    }
                }
            }
        }
        //排序前 遍历输出
        //System.out.println("遍历排序前 该用户评分集合");

        Set<Map.Entry<Integer, Double>> books_interestingSet = books_interesting.entrySet();
        for (Map.Entry<Integer, Double> entry : books_interestingSet) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
        //对感兴趣集合进行排序
        List<Map.Entry<Integer, Double>> books_interesting_list = new ArrayList<Map.Entry<Integer, Double>>(books_interesting.entrySet());
        Collections.sort(books_interesting_list, new Comparator<Map.Entry<Integer, Double>>() {
            public int compare(Map.Entry<Integer, Double> o1,
                               Map.Entry<Integer, Double> o2) {
                if(o1.getValue() < o2.getValue()){
                    return 1;
                }else if(o1.getValue() == o2.getValue()){
                    return 0;
                }else{
                    return -1;
                }
            }
        });
        //排序后遍历输出
        //System.out.println("遍历排序后 该用户评分集合");

        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }

        //books_interesting_list 即为用户1对不同图书的感兴趣程度 集合
        //先去重，将已经在用户recUserid收藏夹中的图书从集合中去掉
        List<Score> usershelf = scoreMapper.selectList(new QueryWrapper<Score>().eq("userid", recUserid));

        List<Map.Entry<Integer, Double>> books_my_list=  new ArrayList<Map.Entry<Integer, Double>>();
        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            for (Score score : usershelf) {
                if(score.getBookid()==entry.getKey())
                {
                    books_my_list.add(entry);
                }
            }
        }
        books_interesting_list.removeAll(books_my_list);
        //去重结束，遍历输出
       // System.out.println("去重 遍历");

        for (Map.Entry<Integer, Double> entry : books_interesting_list) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }

        //返回推荐结果（基于皮尔逊相关系数

        return books_interesting_list;

    }
    @Autowired
    private HistoryMapper historyMapper;
    /**
     * @Author Zhanghf
     * @Date 2021-11-01 19:28
     * @Description: 基于内容的推荐算法对用户产生推荐结果
     * @param userid
     * @Return: java.util.List<com.example.demo.model.Book>
     */
    public List<Book> Content_based(Integer userid)
    {

        List<History> userhistory = historyMapper.selectList(new QueryWrapper<History>().eq("userid", userid));
        ArrayList<Book> historyandshelf = new ArrayList<>();
        for (History history : userhistory) {
            Book book = bookMapper.selectById(history.getBookid());
            historyandshelf.add(book);
        }
        List<BookShelf> bookShelves = bookShelfMapper.selectList(new QueryWrapper<BookShelf>().eq("userid", userid));
        for (BookShelf bookShelf : bookShelves) {
            Book book = bookMapper.selectById(bookShelf.getBookid());
            if (!historyandshelf.contains(book))
            {
                historyandshelf.add(book);
            }
        }
        HashMap<String, Integer> StringIntegerHashMap = new HashMap<>();
        StringIntegerHashMap.put("玄幻奇幻",0);
        StringIntegerHashMap.put("仙侠武侠",0);
        StringIntegerHashMap.put("都市言情",0);
        StringIntegerHashMap.put("穿越历史",0);
        StringIntegerHashMap.put("科幻灵异",0);
        StringIntegerHashMap.put("同人网游",0);
        for (Book book : historyandshelf) {
            switch (book.getType())
            {
                case "玄幻奇幻":
                    StringIntegerHashMap.put("玄幻奇幻",StringIntegerHashMap.get("玄幻奇幻")+1);
                    break;
                case "仙侠武侠":
                    StringIntegerHashMap.put("仙侠武侠",StringIntegerHashMap.get("仙侠武侠")+1);
                    break;
                case "都市言情":
                    StringIntegerHashMap.put("都市言情",StringIntegerHashMap.get("都市言情")+1);
                    break;
                case "穿越历史":
                    StringIntegerHashMap.put("穿越历史",StringIntegerHashMap.get("穿越历史")+1);
                    break;
                case "科幻灵异":
                    StringIntegerHashMap.put("科幻灵异",StringIntegerHashMap.get("科幻灵异")+1);
                    break;
                case "同人网游" :
                    StringIntegerHashMap.put("同人网游",StringIntegerHashMap.get("同人网游")+1);
                    break;
            }
        }
        List<Map.Entry<String, Integer>> list = new ArrayList<>(StringIntegerHashMap.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>()
        {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2)
            {
                //按照value值，用compareTo()方法默认是从小到大排序
                return -o1.getValue().compareTo(o2.getValue());
            }
        });
        //取用户最喜欢的两类图书
        List<Book> top1 = bookMapper.selectList(new QueryWrapper<Book>().eq("type", list.get(0).getKey()).orderByDesc("hits").last("limit 10"));
        List<Book> top2 = bookMapper.selectList(new QueryWrapper<Book>().eq("type", list.get(1).getKey()).orderByDesc("hits").last("limit 10"));
        top1.addAll(top2);
      for (int i=0;i<top1.size();i++)
      {
          for (int j=0;j<historyandshelf.size();j++)
          {
              if (historyandshelf.get(j).equals(top1.get(i)))
              {
                  top1.remove(i);
              }
          }
      }
        return top1;
    }
}
